/*
 * Conectar.c
 *
 *  Created on: Jun 26, 2011
 *      Author: tualibano
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#include <net/if.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>

#include "structs.h"
#include "configurar.h"
#include "menu.h"
#include "busca.h"
#include "arquivo.h"

////////////////////dd//////// T C P ///////////////////////////////////////

void *ConectHandler(void *ptr) {
    struct param *copia = (struct param *) ptr;

    char buffer[32768];
    int numbytes;
    char outrobuffer[32768] = " ";

    Vizinho *novo, n0;
    int cont = 0;
    int cont2 = 0;
    int contavizinhos = 0;

    char *string, ip[17], portatcp[6], portaudp[6];

    ZerarVizinho(&n0);

    novo = NULL;

    strcpy(buffer, "conectar");
    send(copia->socket, buffer, 32, 0);
    while (1) {
        numbytes = recv(copia->socket, buffer, 32768, 0);
        StrFiltraIp(buffer, numbytes);
        printf("\nbuffer>%s\n", buffer);
        // se não caiu a conexão:
        if (numbytes > -1) {
            strcat(outrobuffer, buffer);
            puts("\n até aqui tudo certo\n");
            //puts(outrobuffer);
            //getchar();
            //getchar();
            novo = (struct Vizinho *) malloc(NUMERO_MAX_VINZINHOS
                    * sizeof (struct Vizinho));
            //ExtrairVizinhos(outrobuffer, novo);
            if (outrobuffer != NULL) {
                string = strtok(outrobuffer, " /,");
                while (string != NULL) {
                    if (strlen(string) > 12) {//
                        printf("\nstring:%s \n", string);
                        cont = 0;
                        if (string[0] == ';')
                            cont = 1;
                        cont2 = 0;
                        bzero(portatcp, 5);
                        bzero(portaudp, 5);
                        bzero(ip, 16);
                        while (string[cont] != ':') {
                            ip[cont2] = string[cont];
                            cont++;
                            cont2++;
                        }
                        ip[cont2] = '\0';
                        puts("\nip:");
                        puts(ip);
                        cont2 = 0;
                        cont++;
                        while (string[cont] != ':') {
                            portatcp[cont2] = string[cont];
                            cont++;
                            cont2++;
                        }
                        portatcp[cont2] = '\0';
                        cont2 = 0;
                        puts("\ntcp:");
                        puts(portatcp);
                        cont++;
                        while (string[cont] != ';') {
                            portaudp[cont2] = string[cont];
                            cont++;
                            cont2++;
                        }
                        portaudp[cont2] = '\0';
                        puts("\nudp:");
                        puts(portaudp);
                        printf("\ntodos: %s: %s, %s, %s\n", string, ip,
                                portatcp, portaudp);
                        fflush(stdout);
                        if (contavizinhos < NUMERO_MAX_VINZINHOS) {
                            //copia para a saida.
                            strcpy(novo[contavizinhos].Ip, ip);
                            novo[contavizinhos].PortaTcp = atoi(portatcp);
                            novo[contavizinhos].PortaUdp = atoi(portaudp);
                            contavizinhos++;
                        }
                    }//
                    string = strtok(NULL, " /,");
                }
            }
            //
            cont = 0;
            while (cont < contavizinhos) {
                if (ComparaVizinho(&(novo[cont]), &n0)) {
                    if ((strlen(novo[cont].Ip) > 6))
                        AdicionarVizinho(tabela_vizinhos, &(novo[cont]));
                } else {
                    return (NULL);
                }
                cont++;
            }
        } else
            return (NULL);
    }
    return (NULL);
}

void *ConectarTcp(Vizinho *servidor, int tipo) {

    int SocketLocal, porta;
    char ip[16];
    struct sockaddr_in HostRemoto;
    struct param parametros;
    pthread_t ThreadConectHandler;

    SocketLocal = 0;
    porta = servidor->PortaTcp;
    strcpy(ip, servidor->Ip);

    printf("vo conectar em: %s, %d\n", ip, porta);

    if ((SocketLocal = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    HostRemoto.sin_family = AF_INET;
    HostRemoto.sin_port = htons(porta);
    HostRemoto.sin_addr.s_addr = inet_addr(ip);
    bzero(&(HostRemoto.sin_zero), 8);

    if (connect(SocketLocal, (struct sockaddr *) &HostRemoto,
            sizeof (HostRemoto)) == -1) {
        close(SocketLocal);
    }
    parametros.socket = SocketLocal;
    if (!tipo) {
        // conexão inicial.
        if (!AdicionarVizinho(tabela_vizinhos, servidor))
            puts("este servidor ja foi adicionado");
        pthread_create(&ThreadConectHandler, NULL, ConectHandler,
                (void*) &parametros);
        //pthread_join(ThreadConectHandler, NULL);
    } else {
        //manda pedido de arquivo
    }
    return (NULL);
}

void *ServidorTcp(void *arg) {

    int SocketEscuta, SocketNovaConexao, numbytes, PortaEscuta;
    struct sockaddr_in ip_local;
    struct sockaddr_in ip_remoto;
    unsigned int tamanho;
    char buffer[32768];
    char outrobuffer[32768];

    Vizinho novoviz;

    if ((SocketEscuta = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    PortaEscuta = PORTATCP;

    ip_local.sin_family = AF_INET;
    ip_local.sin_port = htons(PortaEscuta);
    ip_local.sin_addr.s_addr = INADDR_ANY;
    bzero(&(ip_local.sin_zero), 8);

    if (bind(SocketEscuta, (struct sockaddr *) &ip_local,
            sizeof (struct sockaddr)) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(SocketEscuta, BACKLOG) < 0) {
        perror("listen");
        exit(1);
    }

    puts("Servidor criado e na escuta;");
    for (;;) {
        tamanho = sizeof (struct sockaddr_in);
        if ((SocketNovaConexao = accept(SocketEscuta,
                (struct sockaddr *) &ip_remoto, &tamanho)) < 0) {
            perror("accept");
            continue;
        }
        printf("\nServidor: chegando conexão de %s\n", inet_ntoa(
                ip_remoto.sin_addr));

        // chama a thread de utilização de conexão
        // ou gerenciar nova conexão ou mandar arquivo.


        while (1) {
            numbytes = recv(SocketNovaConexao, buffer, 32768, 0);
            if (numbytes != -1) {
                buffer[numbytes] = '\0';
                printf("%s", buffer);
                strcat(outrobuffer, buffer);
                if (strstr(outrobuffer, "arquivo")) {
                    enviararquivo(SocketNovaConexao, outrobuffer);
                    strcpy(outrobuffer, "");
                } else if (strstr(outrobuffer, "conectar")) {
                    // chegou  a palavra conectar
                    // envia os vizinhos desse cara aqui

                    SendTabela(tabela_vizinhos, SocketNovaConexao);
                    // e adiciona como vizinho
                    strcpy(novoviz.Ip, inet_ntoa(ip_remoto.sin_addr));
                    novoviz.PortaTcp = PORTATCP;
                    novoviz.PortaUdp = PORTAUDP;
                    AdicionarVizinho(tabela_vizinhos, &novoviz);
                    strcpy(outrobuffer, "");
                    PrintTabela(tabela_vizinhos);
                    break;
                } else if (strstr(outrobuffer, "resto")) {
                    resto(SocketNovaConexao, outrobuffer);
                    strcpy(outrobuffer, "");
                }
            } else
                break;
        }
    }
    return (NULL);
}

int IniciarServidorTcp() {
    int retThreadServer;
    pthread_t ThreadServer;

    retThreadServer = pthread_create(&ThreadServer, NULL, ServidorTcp,
            (void*) NULL);
    //pthread_join(ThreadServer, NULL);
    return (0);
}

////////////////////////////////////////// U D P //////////////////////////////////

void *ServidorUdp(void *ptr) {
    //struct param *copia = (struct param *) ptr;


    struct sockaddr_in iplocal, ipremoto;
    int socketlocal, porta;
    unsigned int iplen = sizeof (ipremoto);
    char buffer[512], ip[20];
    respostabusca resposta;


    strcpy(ip, "0.0.0.0");

    //puts("\ndigite a porta udp:");
    //scanf("%d", &porta);
    porta = PORTAUDP;

    if ((socketlocal = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        perror("socket");

    iplocal.sin_family = AF_INET;
    iplocal.sin_port = htons(porta);
    iplocal.sin_addr.s_addr = INADDR_ANY;
    bzero(&(iplocal.sin_zero), 8);

    if (bind(socketlocal, (struct sockaddr *) &iplocal, sizeof (iplocal)) == -1)
        perror("bind");

    while (1) {
        if (recvfrom(socketlocal, buffer, 512, 0,
                (struct sockaddr *) &ipremoto, &iplen) == -1)
            perror("recvfrom()");

        printf("\nPacote UDP recebido de %s:%d\nDados: %s\n\n", inet_ntoa(
                ipremoto.sin_addr), ntohs(ipremoto.sin_port), buffer);
        // inserir aqui o processamento dos dados
        // dos pacotes udp.
        // aqui chegará pedidos de busca de aquivos.
        // e outros dados.
        if (strstr(buffer, "busca")) {
            //printf("buffer %s", buffer);
            /*
                                sprintf("\nPacote UDP recebido de %s:%d\nDados: %s\n\n", inet_ntoa(
                                            ipremoto.sin_addr), ntohs(ipremoto.sin_port), buffer);
             */
            //chegou_busca
            buscahandler(buffer);
        }
        //"arquivo":id_busca:iplocal:tamanho:pedaços
        if (strstr(buffer, "arquivo")) {
            puts("\n chegou resposta arquivo \n");
            //chegou_resposta_busca
            // tabela de resposta de busca
            // novabusca = buffer

            //"arquivo":id_busca:nome:tamanho:pedaços:resto
            strtok(buffer, ":"); //arquivo
            strcpy(resposta.idbusca, strtok(NULL, ":")); //id
            strtok(NULL, ":"); //busca
            resposta.tamanho = atoi(strtok(NULL, ":")); //tamanho
            resposta.pedacos = atoi(strtok(NULL, ":")); //pedaços
            resposta.resto = atoi(strtok(NULL, ":")); //resto
            strcpy(resposta.quemtemarquivo.Ip, strtok(NULL, ":"));
            strcpy(resposta.sha1, strtok(NULL, ":"));
            inserirtabelabusca(&tabelabusca, &resposta);
        }

    }
    return (NULL);
}

int IniciarServidorUdp() {
    int retThreadServer;
    pthread_t ThreadServer;

    retThreadServer = pthread_create(&ThreadServer, NULL, ServidorUdp,
            (void*) NULL);
    //pthread_join(ThreadServer, NULL);
    return (0);
}

int CriaSocketUdp(char *ip, int porta) {

    struct sockaddr_in ipremoto;
    int socketremoto;
    //unsigned int iplen = sizeof(struct sockaddr_in);
    //char buffer[512];

    porta = 5001;

    if ((socketremoto = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        perror("socket");

    ipremoto.sin_family = AF_INET;
    ipremoto.sin_port = htons(porta);
    ipremoto.sin_addr.s_addr = inet_addr(ip);
    bzero(&(ipremoto.sin_zero), 8);

    //	sendto(socketremoto, buffer, 512, 0, (struct sockaddr*) &ipremoto, iplen);

    return socketremoto;
}

///////////// LISTAR INTERFACES DE REDE ////////////////

int getiplocal(char *string) {

    struct ifaddrs *myaddrs, *ifa;
    struct sockaddr_in *s4;
    // struct sockaddr_in6 *s6; apenas se ipv6 entrar na jogada.
    int status, cont;
    /* buf must be big enough for an IPv6 address (e.g. 3ffe:2fa0:1010:ca22:020a:95ff:fe8a:1cf8) */
    char buf[64], buffer[32], outrobuffer[256];
    char *listadeip[32];

    status = getifaddrs(&myaddrs);
    if (status != 0) {
        perror("getifaddrs");
        exit(1);
    }

    bzero(outrobuffer, 255);
    for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL)
            continue;
        if ((ifa->ifa_flags & IFF_UP) == 0)
            continue;

        if (ifa->ifa_addr->sa_family == AF_INET) {
            s4 = (struct sockaddr_in *) (ifa->ifa_addr);
            if (inet_ntop(ifa->ifa_addr->sa_family, (void *) &(s4->sin_addr),
                    buf, sizeof (buf)) == NULL) {
                printf("%s: inet_ntop failed!\n", ifa->ifa_name);
            } else {
                sprintf(buffer, "%s:%s;", ifa->ifa_name, buf);
                strcat(outrobuffer, buffer);
            }
        }
        /*  apenas se tiver ipv6 na jogada
         else if (ifa->ifa_addr->sa_family == AF_INET6) {
         s6 = (struct sockaddr_in6 *) (ifa->ifa_addr);
         if (inet_ntop(ifa->ifa_addr->sa_family, (void *) &(s6->sin6_addr),
         buf, sizeof(buf)) == NULL) {
         printf("%s: inet_ntop failed!\n", ifa->ifa_name);
         } else {
         printf("%s: %s\n", ifa->ifa_name, buf);
         }
         }
         */
    }

    freeifaddrs(myaddrs);

    cont = 0;
    printf("\n%s\n", outrobuffer);
    listadeip[cont] = strtok(outrobuffer, ";");
    while (listadeip[cont] != NULL){
        listadeip[cont] = strtok(NULL, ";");
        cont++;
    }
    printf("\nEscolha qual a interface de rede:\n");
    cont = 0;
    while (listadeip[cont] != NULL) {
        printf("\n%d - %s\n",cont,listadeip[cont]);
        cont++;
    }
    fflush(stdout);
    scanf("%d", &cont);

    strtok(listadeip[cont], ":");
    strcpy(iplocal, strtok(NULL, ":"));
    printf("\ninterface escolhida: %s", iplocal);
    //strcpy(string, outrobuffer);
    //////////////////

    return 0;
}
